package com.szcinda.service.dto.history;

import com.szcinda.controller.params.ImportResult;
import com.szcinda.repository.*;
import com.szcinda.service.PageResult;
import com.szcinda.service.SnowFlakeFactory;
import com.szcinda.service.TypeStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Transactional
public class HistoryZadOrderServiceImpl implements HistoryZadOrderService {

    private final HistoryZadOrderRepository historyZadOrderRepository;
    private final OrganizationRepository organizationRepository;
    private final ClientRepository clientRepository;
    private final SnowFlakeFactory snowFlakeFactory = SnowFlakeFactory.getInstance();

    public HistoryZadOrderServiceImpl(HistoryZadOrderRepository historyZadOrderRepository, OrganizationRepository organizationRepository, ClientRepository clientRepository) {
        this.historyZadOrderRepository = historyZadOrderRepository;
        this.organizationRepository = organizationRepository;
        this.clientRepository = clientRepository;
    }

    @Override
    public ImportResult importData(List<HistoryZadOrderImportDto> importDtos) {
        ImportResult importResult = new ImportResult();
        List<Organization> organizations = organizationRepository.findAll();
        List<Client> clients = clientRepository.findAll();
        List<HistoryZadOrderImportDto> historyZadOrderImportDtos = new ArrayList<>();
        // 判断机构是否为空
        int index = 1;
        for (HistoryZadOrderImportDto importDto : importDtos) {
            Assert.isTrue(StringUtils.hasText(importDto.getPackageNo()), String.format("第【%s】行柜号不能为空，请检查数据", index));
            if (StringUtils.hasText(importDto.getOrganizationName())) {
                Assert.isTrue(organizations.stream().anyMatch(organization -> organization.getName().equals(importDto.getOrganizationName())), String.format("系统没有找到机构简称【%s】，请检查数据", importDto.getOrganizationName()));
            }
            Organization organization =
                    organizations.stream().filter(o -> o.getName().equals(importDto.getOrganizationName())).findFirst().get();
            if (TypeStringUtils.historyInType.equals(importDto.getType())) {
                // 填充客户ID
                Optional<Client> optional =
                        clients.stream().filter(client -> client.getOrganizationId().equals(organization.getId()) && client.getName().equals(importDto.getClientName())).findFirst();
                Assert.isTrue(optional.isPresent(), String.format("第【%s】行在机构【%s】下找不到客户【%s】，请检查数据", index,
                        importDto.getOrganizationName(), importDto.getClientName()));
            }
            index++;
        }
        for (HistoryZadOrderImportDto importDto : importDtos) {
            String packageNo = importDto.getPackageNo().replaceAll("　", "");
            List<HistoryZadOrder> historyZadOrderList = historyZadOrderRepository.findByPackageNoAndType(packageNo, importDto.getType());
            // 第一种情况， 如果只有一条记录，并且当前柜号的机构不是导入的机构，则需要提醒
            if (historyZadOrderList != null && historyZadOrderList.size() == 1) {
                // 如果发现其中一条的机构跟需要导入的机构不一致时弹窗提示
                if (historyZadOrderList.stream().anyMatch(historyZadOrder -> !historyZadOrder.getOrganizationName().equals(importDto.getOrganizationName()))) {
                    importDto.setImportErrorMessage(String.format("机构【%s】存在相同柜号【%s】，请确认是否有误", historyZadOrderList.get(0).getOrganizationName(), packageNo));
                    historyZadOrderImportDtos.add(importDto);
                    continue;
                }
            } else if (historyZadOrderList != null && historyZadOrderList.size() > 1) {
                // 第二种情况， 如果有多条记录，并且当前柜号的机构不在历史的机构列表，则需要提醒
                if (historyZadOrderList.stream().noneMatch(historyZadOrder -> historyZadOrder.getOrganizationName().equals(importDto.getOrganizationName()))) {
                    importDto.setImportErrorMessage(String.format("其他机构存在相同柜号【%s】，请确认是否有误", packageNo));
                    historyZadOrderImportDtos.add(importDto);
                    continue;
                }
            }
            HistoryZadOrder historyZadOrder;
            if (historyZadOrderList == null || historyZadOrderList.size() == 0) {
                historyZadOrder = new HistoryZadOrder();
                BeanUtils.copyProperties(importDto, historyZadOrder);
                // 去除空格
                historyZadOrder.setPackageNo(historyZadOrder.getPackageNo().replaceAll("　", ""));
                historyZadOrder.updateDate(importDto);
                historyZadOrder.setId(snowFlakeFactory.nextId("HZ"));
                historyZadOrder.setType(importDto.getType());
                // 填充组织ID
                organizations.stream().filter(organization -> organization.getName().equals(importDto.getOrganizationName())).findFirst().ifPresent(organization -> historyZadOrder.setOrganizationId(organization.getId()));
                // 填充客户ID
                clients.stream().filter(client -> client.getOrganizationId().equals(historyZadOrder.getOrganizationId()) && client.getName().equals(historyZadOrder.getClientName())).findFirst().ifPresent(client -> historyZadOrder.setClientId(client.getId()));
                historyZadOrderRepository.saveAndFlush(historyZadOrder);
            } else {
                historyZadOrderList =
                        historyZadOrderList.stream().filter(hs -> hs.getOrganizationName().equals(importDto.getOrganizationName()) && hs.getPackageNo().equals(packageNo)).collect(Collectors.toList());
                for (HistoryZadOrder zadOrder : historyZadOrderList) {
                    if(1 == zadOrder.getLockStatus()){
                        // 已锁定
                        continue;
                    }
                    // 更新其他信息，费用累加
                    zadOrder.updateBaseInfo(importDto, importDto.getType());
                    // 填充组织ID
                    organizations.stream().filter(organization -> organization.getName().equals(importDto.getOrganizationName())).findFirst().ifPresent(organization -> zadOrder.setOrganizationId(organization.getId()));
                    // 填充客户ID
                    clients.stream().filter(client -> client.getOrganizationId().equals(zadOrder.getOrganizationId()) && client.getName().equals(zadOrder.getClientName())).findFirst().ifPresent(client -> zadOrder.setClientId(client.getId()));
                    historyZadOrderRepository.saveAndFlush(zadOrder);
                }
            }
        }
        importResult.setImportDtos(historyZadOrderImportDtos);
        return importResult;
    }

    private static String getValue(String data1, String data2, boolean append) {
        if (append) {
            String value = "";
            if (StringUtils.hasText(data1)) {
                value += data1;
            }
            if (StringUtils.hasText(data2) && !value.contains(data2)) {
                value = value + "," + data2;
            }
            return value;
        } else {
            if (StringUtils.isEmpty(data1)) {
                return data2;
            } else {
                return data1;
            }
        }
    }

    private static double add(double d1, double d2) {
        return BigDecimal.valueOf(d1).add(BigDecimal.valueOf(d2)).setScale(2, RoundingMode.HALF_UP).doubleValue();
    }

    @Override
    public PageResult<HistoryZadOrder> query(HisZadOrderQueryDto params) {
        Specification<HistoryZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Predicate type = criteriaBuilder.like(root.get("type"), params.getType());
            predicates.add(type);
            if (!StringUtils.isEmpty(params.getOrganizationName())) {
                Predicate organizationName = criteriaBuilder.like(root.get("organizationName"), "%" + params.getOrganizationName().trim() + "%");
                predicates.add(organizationName);
            }
            if (!StringUtils.isEmpty(params.getBookNo())) {
                Predicate bookNo = criteriaBuilder.like(root.get("bookNo"), "%" + params.getBookNo().trim() + "%");
                predicates.add(bookNo);
            }
            if (!StringUtils.isEmpty(params.getOrganizationId())) {
                Predicate organizationId = criteriaBuilder.equal(root.get("organizationId"), params.getOrganizationId());
                predicates.add(organizationId);
            }
            if (!StringUtils.isEmpty(params.getClientName())) {
                Predicate clientName = criteriaBuilder.like(root.get("clientName"), "%" + params.getClientName().trim() + "%");
                predicates.add(clientName);
            }
            if (!StringUtils.isEmpty(params.getCarrierName())) {
                Predicate clientName = criteriaBuilder.like(root.get("carrierName"), "%" + params.getCarrierName().trim() + "%");
                predicates.add(clientName);
            }
            if (!StringUtils.isEmpty(params.getBusinessType())) {
                Predicate businessType = criteriaBuilder.like(root.get("businessType"), "%" + params.getBusinessType().trim() + "%");
                predicates.add(businessType);
            }
            if (!StringUtils.isEmpty(params.getCalculateType())) {
                Predicate calculateType = criteriaBuilder.like(root.get("calculateType"), "%" + params.getCalculateType().trim() + "%");
                predicates.add(calculateType);
            }
            if (!StringUtils.isEmpty(params.getPackageNo())) {
                Predicate packageNo = criteriaBuilder.like(root.get("packageNo"), "%" + params.getPackageNo().trim() + "%");
                predicates.add(packageNo);
            }
            if (params.getBlTimeStart() != null) {
                Predicate timeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("blTime"), params.getBlTimeStart());
                predicates.add(timeStart);
            }
            if (params.getBlTimeEnd() != null) {
                Predicate timeEnd = criteriaBuilder.lessThan(root.get("blTime"), params.getBlTimeEnd().plusDays(1));
                predicates.add(timeEnd);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime");
        Pageable pageable = new PageRequest(params.getPage() - 1, params.getPageSize(), order);
        Page<HistoryZadOrder> details = historyZadOrderRepository.findAll(specification, pageable);
        if (TypeStringUtils.historyOutType.equals(params.getType())) {
            for (HistoryZadOrder historyZadOrder : details.getContent()) {
                // 拼接供应商
                historyZadOrder.updateCarrierName();
            }
        }
        return PageResult.of(details.getContent(), params.getPage(), params.getPageSize(), details.getTotalElements());
    }

    @Override
    public void delete(String id) {
        historyZadOrderRepository.delete(id);
    }

    @Override
    public void createNew(List<HistoryZadOrderImportDto> zadOrderImportDtoList) {
        List<HistoryZadOrder> historyZadOrders = new ArrayList<>();
        List<Organization> organizations = organizationRepository.findAll();
        List<Client> clients = clientRepository.findAll();
        for (HistoryZadOrderImportDto importDto : zadOrderImportDtoList) {
            HistoryZadOrder historyZadOrder = new HistoryZadOrder();
            BeanUtils.copyProperties(importDto, historyZadOrder);
            historyZadOrder.setId(snowFlakeFactory.nextId("HZ"));
            organizations.stream().filter(organization -> organization.getName().equals(importDto.getOrganizationName()))
                    .findFirst()
                    .ifPresent(organization -> {
                        historyZadOrder.setOrganizationId(organization.getId());
                        clients.stream().filter(client -> client.getOrganizationId().equals(organization.getId()) && client.getName().equals(importDto.getClientName()))
                                .findFirst()
                                .ifPresent(client -> historyZadOrder.setClientId(client.getId()));
                    });
            historyZadOrder.setType(importDto.getType());
            historyZadOrders.add(historyZadOrder);
        }
        historyZadOrderRepository.save(historyZadOrders);
    }

    @Override
    public List<HistoryZadOrder> listByIds(List<String> ids) {
        Specification<HistoryZadOrder> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Expression<String> exp = root.get("id");
            predicates.add(exp.in(ids));
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime");
        return historyZadOrderRepository.findAll(specification, order);
    }

    @Override
    public void lock(List<String> ids) {
        List<HistoryZadOrder> historyZadOrders = historyZadOrderRepository.findAll(ids);
        for (HistoryZadOrder historyZadOrder : historyZadOrders) {
            historyZadOrder.setLockStatus(1);
        }
        historyZadOrderRepository.save(historyZadOrders);
    }

    @Override
    public void unlock(List<String> ids) {
        List<HistoryZadOrder> historyZadOrders = historyZadOrderRepository.findAll(ids);
        for (HistoryZadOrder historyZadOrder : historyZadOrders) {
            historyZadOrder.setLockStatus(0);
        }
        historyZadOrderRepository.save(historyZadOrders);
    }
}
